'root'
   Program(-> P)
   print(P)

/* Abstract Syntax */

'type' PROG
   prog (DECLARATIONS_LIST, STATEMENT, EXPR)

'type' DECLARATION
   singleDeclaration (IDENT, TYPE)
   arrayDeclaration (IDENT, TYPE, INT)
   definition (IDENT, TYPE, EXPR)

'type' DECLARATIONS_LIST
   declarationsList (DECLARATION, DECLARATIONS_LIST)
   nil

'type' TYPE
   integer
   unsigned

'type' VARIABLE
   singleVar(IDENT)
   arrayElem(IDENT,EXPR)

'type' STATEMENT
   assign (VARIABLE, EXPR)
   predec(VARIABLE)
   preinc(VARIABLE)
   postdec(VARIABLE)
   postinc(VARIABLE)
   cond1 (EXPR,STATEMENT,STATEMENT)
   cond2 (EXPR,STATEMENT)
   loop (EXPR,STATEMENT)
   block (STATEMENT)
   sequence (STATEMENT, STATEMENT)
   emptyStatement

'type' EXPR
   lor(EXPR,EXPR)
   land(EXPR,EXPR)
   equal(EXPR,EXPR)
   nequal(EXPR,EXPR)
   lessthan(EXPR,EXPR)
   lessequal(EXPR,EXPR)
   greaterthan(EXPR,EXPR)
   greaterequal(EXPR,EXPR)
   plus(EXPR, EXPR)
   minus(EXPR, EXPR)
   mul(EXPR, EXPR)
   lneg(EXPR)
   neg(EXPR)
   pos(EXPR)
   predec(VARIABLE)
   preinc(VARIABLE)
   postdec(VARIABLE)
   postinc(VARIABLE)
   num(INT)
   var(VARIABLE)

'type' IDENT


/* Concrete Syntax */

'nonterm' Program(-> PROG)
   'rule' Program(-> prog(Ds, S, RV)) : DeclarationsList(-> Ds) StatementsSequence(-> S) "return" Expr(-> RV) ";"
   'rule' Program(-> prog(nil, S, RV)) : StatementsSequence(-> S) "return" Expr(-> RV) ";"
   'rule' Program(-> prog(Ds, emptyStatement, RV)) : DeclarationsList(-> Ds) "return" Expr(-> RV) ";"
   'rule' Program(-> prog(nil, emptyStatement, RV)) : "return" Expr(-> RV) ";"

'nonterm' Type(-> TYPE)
   'rule' Type(-> unsigned) : "unsigned" "int"
   'rule' Type(-> unsigned) : "unsigned"
   'rule' Type(-> integer) : "int"

'nonterm' Declaration(-> DECLARATION)
   'rule' Declaration(-> definition(I, T, V)) : Type(-> T) Ident(-> I) "=" Expr(-> V) ";"
   'rule' Declaration(-> arrayDeclaration(I, T, S)) : Type(-> T) "[" number(-> S) "]" Ident(-> I) ";" ge(S,0)
   'rule' Declaration(-> arrayDeclaration(I, T, S)) : Type(-> T) Ident(-> I) "[" number(-> S) "]" ";" ge(S,0)
   'rule' Declaration(-> singleDeclaration(I, T)) : Type(-> T) Ident(-> I) ";"

'nonterm' DeclarationsList(-> DECLARATIONS_LIST)
   'rule' DeclarationsList(-> declarationsList(D, Ds)) : Declaration(-> D) DeclarationsList(-> Ds)
   'rule' DeclarationsList(-> declarationsList(D, nil)) : Declaration(-> D)

'nonterm' Expr(-> EXPR)
   'rule' Expr(-> X) : Expr2(-> X)

'nonterm' Expr2(-> EXPR)
   'rule' Expr2(-> X) : Expr3(-> X)
   'rule' Expr2(-> lor(X, Y)) : Expr2(-> X) "||" Expr3(-> Y)

'nonterm' Expr3(-> EXPR)
   'rule' Expr3(-> X) : Expr4(-> X)
   'rule' Expr3(-> land(X, Y)) : Expr3(-> X) "&&" Expr4(-> Y)

'nonterm' Expr4(-> EXPR)
   'rule' Expr4(-> X) : Expr5(-> X)
   'rule' Expr4(-> equal(X, Y)) : Expr4(-> X) "==" Expr5(-> Y)
   'rule' Expr4(-> nequal(X, Y)) : Expr4(-> X) "!=" Expr5(-> Y)

'nonterm' Expr5(-> EXPR)
   'rule' Expr5(-> X) : Expr6(-> X)
   'rule' Expr5(-> lessthan(X, Y)) : Expr5(-> X) "<" Expr6(-> Y)
   'rule' Expr5(-> lessequal(X, Y)) : Expr5(-> X) "<=" Expr6(-> Y)
   'rule' Expr5(-> greaterthan(X, Y)) : Expr5(-> X) ">" Expr6(-> Y)
   'rule' Expr5(-> greaterequal(X, Y)) : Expr5(-> X) ">=" Expr6(-> Y)

'nonterm' Expr6(-> EXPR)
   'rule' Expr6(-> X) : Expr7(-> X)
   'rule' Expr6(-> plus(X, Y)) : Expr6(-> X) "+" Expr7(-> Y)
   'rule' Expr6(-> minus(X, Y)): Expr6(-> X) "-" Expr7(-> Y)

'nonterm' Expr7(-> EXPR)
   'rule' Expr7(-> X) : Expr8 (-> X)
   'rule' Expr7(-> mul(X, Y)) : Expr7(-> X) "*" Expr8(-> Y)

'nonterm' Expr8(-> EXPR)
   'rule' Expr8(-> X) : Expr9(-> X)
   'rule' Expr8(-> lneg(X)) : "!" Expr9(-> X)
   'rule' Expr8(-> pos(X)) : "+" Expr9(-> X)
   'rule' Expr8(-> neg(X)) : "-" Expr9(-> X)
   'rule' Expr8(-> preinc(X)) : "++" Variable(-> X)
   'rule' Expr8(-> postinc(X)) : Variable(-> X) "++"
   'rule' Expr8(-> predec(X)) : "--" Variable(-> X)
   'rule' Expr8(-> postdec(X)) : Variable(-> X) "--"

'nonterm' Expr9 (-> EXPR)
   'rule' Expr9 (-> X) Expr10(-> X)
   'rule' Expr9 (-> postinc(X)) : Variable (-> X) "++"
   'rule' Expr9 (-> postdec(X)) : Variable (-> X) "--"

'nonterm' Expr10(-> EXPR)
   'rule' Expr10(-> num(X)) : number(-> X)
   'rule' Expr10(-> var(X)) : Variable(-> X)
   'rule' Expr10(-> X) : "(" Expr(-> X) ")"

'nonterm' Variable(-> VARIABLE)
   'rule' Variable(-> singleVar(I)) : Ident(-> I)
   'rule' Variable(-> arrayElem(I,N)) : Ident(-> I) "[" Expr(-> N) "]"

'nonterm' StatementsSequence(-> STATEMENT)
   'rule' StatementsSequence(-> S) : Statement(-> S)
   'rule' StatementsSequence(-> sequence(S1, S2)) : Statement(-> S1) StatementsSequence(-> S2)

'nonterm' Statement(-> STATEMENT)
   'rule' Statement(-> assign(V, E)) : Variable(-> V) "=" Expr(-> E) ";"
   'rule' Statement(-> preinc(V)) : "++" Variable(-> V) ";"
   'rule' Statement(-> postinc(V)) : Variable(-> V) "++" ";"
   'rule' Statement(-> predec(V)) : "--" Variable(-> V) ";"
   'rule' Statement(-> postdec(V)) : Variable(-> V) "--" ";"
   'rule' Statement(-> cond1(E, S1, S2)) : "if" "(" Expr(-> E) ")" Statement(-> S1) "else" Statement(-> S2)
   'rule' Statement(-> cond2(E, S1)) : "if" "(" Expr(-> E) ")" Statement(-> S1)
   'rule' Statement(-> loop(E, S)) : "while" "(" Expr(-> E) ")" Statement(-> S)
   'rule' Statement(-> block(S)) : "{" StatementsSequence(-> S) "}"
   'rule' Statement(-> emptyStatement) : ";"

'nonterm' number(-> INT)
	'rule' number(-> X) : Unsigned(-> X)
	'rule' number(-> - X) : "-" Unsigned(-> X)

'token' Ident (-> IDENT)
'token' Unsigned (-> INT)
